package uk.co.caprica.vlcj.test.oop.buttons;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

public abstract class MediaButton extends JPanel {

    private static final long serialVersionUID = 1L;

    public static String ACTIVE_PROPERTY = "active";

    private static final int horizontalPadding = 12;

    private static final int verticalPadding = 8;

    private static final int arcWidth = 12;

    private static final int arcHeight = 12;

    private final Stroke borderStroke = new BasicStroke(1.5f);

    private final Color fillColour = new Color(0, 0, 0);

    private final Color borderColour = new Color(128, 128, 128);

    private final String actionCommand;

    private final Shape shape;

    private final Paint shapePaint;

    private final Paint shapeActivePaint;

    private BufferedImage image;

    private BufferedImage activeImage;

    private boolean pressed;

    private boolean active;

    public MediaButton() {
        this.actionCommand = getActionCommand();
        this.shape = getShape();
        this.shapePaint = getShapePaint();
        this.shapeActivePaint = getShapeActivePaint();

        setBorder(new EmptyBorder(2, 2, 2, 2));
        setBackground(Color.black);
        setOpaque(false);

        addMouseListener(new MediaButtonMouseListener());
    }

    public void addActionListener(ActionListener listener) {
        listenerList.add(ActionListener.class, listener);
    }

    public void removeActionListener(ActionListener listener) {
        listenerList.add(ActionListener.class, listener);
    }

    @Override
    public void paintComponent(Graphics g) {
        prepareImages();

        Graphics2D g2 = (Graphics2D)g;

        if(pressed) {
            g2.translate(1, 1);
        }

        // Paint the button image
        g2.drawImage(!active ? image : activeImage, null, 0, 0);

        // Overlay a border
        Insets insets = getInsets();

        int x = insets.left;
        int y = insets.top;
        int w = getWidth() - (insets.left + insets.top) - 1;
        int h = getHeight() - (insets.top + insets.bottom) - 1;

        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        Stroke oldStroke = g2.getStroke();
        g2.setColor(borderColour);
        g2.setStroke(borderStroke);
        g2.drawRoundRect(x, y, w, h, arcWidth, arcHeight);
        g2.setStroke(oldStroke);
    }

    @Override
    public Dimension getPreferredSize() {
        int w = horizontalPadding;
        int h = verticalPadding;

        Insets insets = getInsets();
        w += insets.left + insets.right;
        h += insets.top + insets.bottom;

        Rectangle2D shapeBounds = shape.getBounds2D();
        w += shapeBounds.getWidth();
        h += shapeBounds.getHeight();

        return new Dimension(w, h);
    }

    private void prepareImages() {
        if(image == null) {
            image = paintShape(false);
            activeImage = paintShape(true);
        }
    }

    private BufferedImage paintShape(boolean active) {
        // First create an image of the right size
        Rectangle2D shapeBounds = shape.getBounds2D();
        int imageWidth = horizontalPadding + (int)Math.ceil(shapeBounds.getWidth()) + horizontalPadding;
        int imageHeight = verticalPadding + (int)Math.ceil(shapeBounds.getHeight()) + verticalPadding;
        BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = (Graphics2D)image.getGraphics();

        // Paint the component
        Insets insets = getInsets();

        int x = insets.left;
        int y = insets.top;
        int w = getWidth() - (insets.left + insets.top);
        int h = getHeight() - (insets.top + insets.bottom);

        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        g2.setColor(fillColour);
        g2.fillRoundRect(x, y, w, h, arcWidth, arcHeight);

        int shapeX = (int)Math.round((getWidth() - shapeBounds.getWidth()) / 2);
        int shapeY = (int)Math.round((getHeight() - shapeBounds.getHeight()) / 2);
        g2.translate(shapeX, shapeY);

        if(!active) {
            g2.setPaint(shapePaint);
            g2.fill(shape);
        }
        else {
            g2.setPaint(shapeActivePaint);
            g2.fill(shape);
            // GlowFilter glowFilter = new GlowFilter();
            // glowFilter.setAmount(1.1f);
            // glowFilter.setRadius(15.0f);
            // glowFilter.filter(image, image);
        }

        g2.dispose();

        return image;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        firePropertyChange(ACTIVE_PROPERTY, this.active, this.active = active);
        repaint();
    }

    protected abstract String getActionCommand();

    protected abstract Shape getShape();

    protected abstract Paint getShapePaint();

    // TODO this is no longer used, get rid
    protected abstract Paint getShapeActivePaint();

    private final class MediaButtonMouseListener extends MouseAdapter {

        @Override
        public void mouseEntered(MouseEvent e) {
        }

        @Override
        public void mousePressed(MouseEvent e) {
            pressed = true;
            repaint();
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            pressed = false;
            repaint();
        }

        @Override
        public void mouseClicked(MouseEvent e) {
            ActionListener[] listeners = listenerList.getListeners(ActionListener.class);
            if(listeners.length > 0) {
                ActionEvent evt = new ActionEvent(MediaButton.this, ActionEvent.ACTION_PERFORMED, actionCommand, System.currentTimeMillis(), e.getModifiers());
                for(int i = listeners.length - 1; i >= 0; i -- ) {
                    listeners[i].actionPerformed(evt);
                }
            }
        }

        @Override
        public void mouseExited(MouseEvent e) {
            repaint();
        }
    }
}
